"""
重试机制 (Retry)

实现智能重试策略，提高请求成功率。

支持的重试策略：
1. 指数退避 (Exponential Backoff) - 逐步增加等待时间
2. 固定延迟 (Fixed Delay) - 固定间隔重试
3. 立即重试 (Immediate) - 无延迟重试

作者: RedFire Team
创建日期: 2025-10-06
版本: v1.0
"""

from abc import ABC, abstractmethod
from typing import Optional, Callable, Any, Type, Tuple
import asyncio
import logging
import random


class RetryPolicy(ABC):
    """
    重试策略抽象基类
    """
    
    def __init__(
        self,
        max_attempts: int = 3,
        retryable_exceptions: Optional[Tuple[Type[Exception], ...]] = None,
        logger: Optional[logging.Logger] = None
    ):
        """
        初始化重试策略
        
        Args:
            max_attempts: 最大尝试次数
            retryable_exceptions: 可重试的异常类型
            logger: 日志记录器
        """
        self.max_attempts = max_attempts
        self.retryable_exceptions = retryable_exceptions or (Exception,)
        self.logger = logger or logging.getLogger(__name__)
    
    @abstractmethod
    def get_delay(self, attempt: int) -> float:
        """
        获取重试延迟时间
        
        Args:
            attempt: 当前尝试次数（从1开始）
        
        Returns:
            float: 延迟时间（秒）
        """
        pass
    
    def should_retry(self, exception: Exception, attempt: int) -> bool:
        """
        判断是否应该重试
        
        Args:
            exception: 发生的异常
            attempt: 当前尝试次数
        
        Returns:
            bool: 是否应该重试
        """
        if attempt >= self.max_attempts:
            return False
        
        return isinstance(exception, self.retryable_exceptions)
    
    async def execute(self, func: Callable, *args, **kwargs) -> Any:
        """
        执行带重试的函数调用
        
        Args:
            func: 要执行的函数（可以是同步或异步）
            *args: 位置参数
            **kwargs: 关键字参数
        
        Returns:
            Any: 函数返回值
        
        Raises:
            最后一次调用的异常
        """
        last_exception = None
        
        for attempt in range(1, self.max_attempts + 1):
            try:
                # 支持异步和同步函数
                if asyncio.iscoroutinefunction(func):
                    result = await func(*args, **kwargs)
                else:
                    result = func(*args, **kwargs)
                
                if attempt > 1:
                    self.logger.info(f"重试成功: 第 {attempt} 次尝试")
                
                return result
                
            except Exception as e:
                last_exception = e
                
                if not self.should_retry(e, attempt):
                    self.logger.error(f"不可重试或达到最大重试次数: {e}")
                    raise
                
                if attempt < self.max_attempts:
                    delay = self.get_delay(attempt)
                    self.logger.warning(
                        f"第 {attempt} 次尝试失败: {e}, "
                        f"{delay:.2f}秒后重试..."
                    )
                    await asyncio.sleep(delay)
                else:
                    self.logger.error(f"所有重试尝试失败: {e}")
                    raise
        
        # 不应该到达这里
        if last_exception:
            raise last_exception


class ExponentialBackoffRetry(RetryPolicy):
    """
    指数退避重试策略
    
    特点：
    - 重试延迟指数增长
    - 可添加随机抖动（jitter）避免惊群
    - 适合网络请求、API调用
    
    公式：
    delay = base_delay * (multiplier ** (attempt - 1)) + jitter
    
    使用示例：
    ```python
    retry = ExponentialBackoffRetry(
        max_attempts=5,
        base_delay=1.0,
        max_delay=60.0,
        jitter=True
    )
    
    result = await retry.execute(some_api_call, arg1, arg2)
    ```
    """
    
    def __init__(
        self,
        max_attempts: int = 3,
        base_delay: float = 1.0,
        max_delay: float = 60.0,
        multiplier: float = 2.0,
        jitter: bool = True,
        retryable_exceptions: Optional[Tuple[Type[Exception], ...]] = None,
        logger: Optional[logging.Logger] = None
    ):
        """
        初始化指数退避重试策略
        
        Args:
            max_attempts: 最大尝试次数
            base_delay: 基础延迟时间（秒）
            max_delay: 最大延迟时间（秒）
            multiplier: 延迟倍增系数
            jitter: 是否添加随机抖动
            retryable_exceptions: 可重试的异常类型
            logger: 日志记录器
        """
        super().__init__(max_attempts, retryable_exceptions, logger)
        
        self.base_delay = base_delay
        self.max_delay = max_delay
        self.multiplier = multiplier
        self.jitter = jitter
    
    def get_delay(self, attempt: int) -> float:
        """
        计算指数退避延迟
        
        Args:
            attempt: 当前尝试次数
        
        Returns:
            float: 延迟时间（秒）
        """
        # 计算基础延迟
        delay = self.base_delay * (self.multiplier ** (attempt - 1))
        
        # 限制最大延迟
        delay = min(delay, self.max_delay)
        
        # 添加随机抖动
        if self.jitter:
            jitter_amount = delay * 0.1  # 10% 抖动
            delay += random.uniform(-jitter_amount, jitter_amount)
        
        return max(0.0, delay)


class FixedDelayRetry(RetryPolicy):
    """
    固定延迟重试策略
    
    特点：
    - 固定间隔重试
    - 简单可预测
    - 适合快速重试场景
    
    使用示例：
    ```python
    retry = FixedDelayRetry(
        max_attempts=3,
        delay=2.0
    )
    
    result = await retry.execute(database_query)
    ```
    """
    
    def __init__(
        self,
        max_attempts: int = 3,
        delay: float = 1.0,
        retryable_exceptions: Optional[Tuple[Type[Exception], ...]] = None,
        logger: Optional[logging.Logger] = None
    ):
        """
        初始化固定延迟重试策略
        
        Args:
            max_attempts: 最大尝试次数
            delay: 固定延迟时间（秒）
            retryable_exceptions: 可重试的异常类型
            logger: 日志记录器
        """
        super().__init__(max_attempts, retryable_exceptions, logger)
        self.delay = delay
    
    def get_delay(self, attempt: int) -> float:
        """
        返回固定延迟
        
        Args:
            attempt: 当前尝试次数
        
        Returns:
            float: 固定延迟时间
        """
        return self.delay


class ImmediateRetry(RetryPolicy):
    """
    立即重试策略
    
    特点：
    - 无延迟立即重试
    - 最快的重试方式
    - 适合瞬时故障
    
    使用示例：
    ```python
    retry = ImmediateRetry(max_attempts=3)
    result = await retry.execute(quick_operation)
    ```
    """
    
    def get_delay(self, attempt: int) -> float:
        """
        返回零延迟
        
        Args:
            attempt: 当前尝试次数
        
        Returns:
            float: 0.0
        """
        return 0.0

